W12_作業一實作記錄 [ MTR05 ] 實作之三


Posted by Christy on 2021-08-30

1. hw1_實作 API 新增留言

MTR05_第十二週(06/28 ~ 07/04):前後端整合_作業一帶著做之打造後端 API:Part1

流程:

a. 先引入連線

b. 引入 header('Content-Type: application/json;charset=utf-8'); 把資料變成 json 格式

c. 錯誤處理:如果沒有 content, nickname, site_key 就回傳錯誤訊息

if (
    empty($_POST['content']) ||
    empty($_POST['nickname']) ||
    empty($_POST['site_key'])
  ) {
    $json = array(
      "ok" => false,
      "message" => "Please input missing fileds"
    );

    $response = json_encode($json);
    echo $response;
    die();
  }

d. 把資料拿進來以後,新增到資料庫裡面

e. 如果沒有資料,做錯誤處理

f. 用 postman 做 API 測試,線上版的不能用,後來下載了桌面版的,在下面選 desktop agent,因為非同源政策的關係,不可以用瀏覽器版本的

遇到的困難:

  1. 跟著老師打程式碼,真的就是要細心!有些錯誤自己看半天都找不到,就這樣卡了一小時,沒關係,找到就好!(成功了真的好開心)

2. hw1_實作 API 顯示留言

MTR05_第十二週(06/28 ~ 07/04):前後端整合_作業一帶著做之打造後端 API:Part1_13':15"

流程:

a. 引入連線還有 header('Content-type: application/json;charset=utf-8');

b. 錯誤處理,檢查 $_GET[] 有沒有拿到東西;在這裡我們拿的資料是 site_key

c. 我們現在用的是 GET 方法拿到 site_key

d. sql 拿資料:要 nickname, content, created_at(記得一定要用防 sql injection 的方法 stmt prepare)

e. 錯誤處理:如果沒有結果的話,就回傳錯誤訊息

f. 為什麼要用get_result():

當使用 stmt prepare 且需要得到回傳的結果時,就要用這個函式

mysqli_stmt::get_result:
Gets a result set from a prepared statement as a mysqli_result object

g. 為什麼要用 array_push():

抓到資料以後,要把資料放到 array 裡面,用法:

<?php
  $stack = array("orange", "banana");
  array_push($stack, "apple", "raspberry");
  print_r($stack);
?>

// 上面會輸出
Array
(
  [0] => orange
  [1] => banana
  [2] => apple
  [3] => raspberry
)

遇到的困難:

  1. 永遠檢查 sql 語法,方法就是把它丟進 mysql 裡面檢查

  2. array 裡面永遠要用逗號分隔


程式碼:

<?php  
  require_once('conn.php');
  header('Content-type: application/json;charset=utf-8');
  if (
    empty($_GET['site_key'])
  ) {
    $json = array(
      "ok" => false,
      "message" => "Please add site_key in url"
    );

    $response = json_encode($json);
    echo $response;
    die();
  }

  $site_key = $_GET['site_key'];

  $sql = "select nickname, content, created_at from discussions where site_key = ? order by id desc";
  $stmt = $conn->prepare($sql);
  $stmt->bind_param('s', $site_key);
  $result = $stmt->execute();

  if (!$result) {
   $json = array(
     "ok" =>  false,
     "message" => $conn->error
    );

    $response = json_encode($json);
    echo $response;
    die(); 
  }

  $result = $stmt->get_result();
  $discussions = array();
  while($row = $result->fetch_assoc()) {
    array_push($discussions, array(
      "nickname" => $row["nickname"],
      "content" => $row["content"],
      "created_at" => $row["created_at"]
    ));
  }
  $json = array(
    "ok" => true,
    "discussions" => $discussions
  );

  $response = json_encode($json);
  echo $response;
?>

3. hw1_實作 API 串接

MTR05_第十二週(06/28 ~ 07/04):前後端整合_作業一帶著做之打造後端 API:Part2

a. 先用 bootstrap 刻 UI: 就是去找適合的物件貼上

b. 串 API 04':50"

b.1 先顯示留言資料:

一定開始要用 $(document).ready()

  <script>
    function escape(toOutput){
      return toOutput.replace(/\&/g, '&amp;')
          .replace(/\</g, '&lt;')
          .replace(/\>/g, '&gt;')
          .replace(/\"/g, '&quot;')
          .replace(/\'/g, '&#x27')
          .replace(/\//g, '&#x2F');
    }

    $(document).ready(() => {
      $.ajax({
        url: "http://localhost:8080/christy/discussions/api_comments.php?site_key=christy",
      }).done(function(data) {
        if (!data.ok) {
          alert(data.message)
          return
        }

        const comments = data.discussions;
        for (let comment of comments) {
          $('.comments').append(`
            <div class="card mt-3">
              <div class="card-body">
                <h5 class="card-title">${escape(comment.nickname)}</h5>
                <p class="card-text">${escape(comment.content)}</p>
              </div>
            </div>
          `)
        }
      });
    })
  </script>

b.2 新增留言:

先監聽表單送出事件,可以查 You might not need jQuery 要怎麼 POST

遇到的困難:

  1. chrome 的 dev tool,如果把 selected context only 打勾,就不會顯示錯誤訊息了

  2. 因為非同源政策,所以要加一個 header 在自己寫的 API 檔案裡面

header('Access-Control-Allow-Origin: *');

  1. 因為要用 jQuery,所以要引入在 head 標籤裡;當然要記得引入 bootstrap

  2. 就算跟著老師一起做,還是會有打錯的地方,如果錯了,記得一定一定要停下來訂正,不要繼續跟著走,不然碼越多就越找不到

這次的錯誤是在用 POST 的時候,裡面的data: newCommentData,前面的 data 忘記打。

雖然影片裡面老師說,ES6 參數一樣就不用打,但是這裡參數不一樣!

天阿,找到錯誤好感動!

$.ajax({
          type: 'POST',
          url: 'http://localhost:8080/christy/discussions/api_add_comments.php',
          data: newCommentData
        }).done(function(data) {
          if (!data.ok) {
            alert(data.message)
            return
          }

4. hw1_實作API「載入更多」

MTR05_第十二週(06/28 ~ 07/04):前後端整合_作業一參考範例(請寫完作業再看)

流程:

有載入更多的按鈕,按下去以後會顯示多五筆留言。

留言順序由新到舊排列,留言大於五筆,才會出現載入更多的按鈕,按下去就會顯示多五筆留言;沒有更多留言時,就不會有載入更多的按鈕。

  1. 先去拿 before 這個資料,把 sql 分成兩種情況:

a. 網址參數有 before

b. 網址參數沒有 before

  1. 記得要帶 id

小結:API 就寫成兩種情況,如果在 postman 可以跑就差不多了


  1. 分頁功能有兩種,page-based, cursor-based
  • page-based 就是有分頁按鈕形式,可以任意跳到某一頁,每一頁的留言筆數是固定的那種

  • cursor-based 的重點在於 before and after

如果 API URL 帶的參數為:...?before=6,就會顯示留言筆數 5, 4, 3, 2, 1

參數為:...?after=5,就會顯示留言筆數為 6, 7, 8, 9, 10

  1. 04':00" - 11':50" 實作 api 載入更多

a. 根據 before 是不是空的,決定要帶什麼參數

我覺得重點觀念是有 before 跟 after 這兩個東西,難理解的觀念是 before 是哪裡來的?sql 語法怎麼寫?在網址上帶參數時,要怎麼規範?

無法再看第三次的時候理解,但是先繼續下去。

遇到的問題:

  1. 在 postman 檢查的時候,要注意 URL 的路徑

  • 老師的程式碼,但是沒有防 sql injection,我下面有改寫。
<?php  
  require_once('conn.php');
  header('Content-type: application/json;charset=utf-8');
  header('Access-Control-Allow-Origin: *');
  if (
    empty($_GET['site_key'])
  ) {
    $json = array(
      "ok" => false,
      "message" => "Please add site_key in url"
    );

    $response = json_encode($json);
    echo $response;
    die();
  }

  $site_key = $_GET['site_key'];

  $sql = 
    "select id, nickname, content, created_at from discussions where site_key = ? " .
    (empty($_GET['before']) ? "" : "and id < ? ") . 
    "order by created_at desc limit 5";

  $stmt = $conn->prepare($sql);
  if (empty($_GET['before'])) {
    $stmt->bind_param('s', $site_key);
  } else {
    $stmt->bind_param('si', $site_key, $_GET['before']);
  }

  $result = $stmt->execute();

  if (!$result) {
   $json = array(
     "ok" =>  false,
     "message" => $conn->error
    );

    $response = json_encode($json);
    echo $response;
    die(); 
  }

  $result = $stmt->get_result();
  $discussions = array();
  while($row = $result->fetch_assoc()) {
    array_push($discussions, array(
      "id" => $row["id"],
      "nickname" => $row["nickname"],
      "content" => $row["content"],
      "created_at" => $row["created_at"]
    ));
  }
  $json = array(
    "ok" => true,
    "discussions" => $discussions
  );

  $response = json_encode($json);
  echo $response;
?>

  • 我的程式碼,有防 sql injection
<?php  
  require_once('conn.php');
  header('Content-type: application/json;charset=utf-8');
  header('Access-Control-Allow-Origin: *');
  if (
    empty($_GET['site_key'])
  ) {
    $json = array(
      'ok' => false,
      'message' => 'Please add site_key in url'
    );

    $response = json_encode($json);
    echo $response;
    die();
  }

  $site_key = $_GET['site_key'];
  $before = $_GET['before'];

  if (!$before) {
    $sql = 'select id, nickname, content, created_at from discussions where site_key = ? order by id desc limit 5';
    $stmt = $conn->prepare($sql);
    $stmt->bind_param('s', $site_key);
  } else {
    $sql = 'select id, nickname, content, created_at from discussions where site_key = ? and id < ? order by id desc limit 5';
    $stmt = $conn->prepare($sql);
    $stmt->bind_param('si', $site_key, $before);
  }

  $result = $stmt->execute();

  if (!$result) {
   $json = array(
     'ok' =>  false,
     'message' => $conn->error
    );

    $response = json_encode($json);
    echo $response;
    die(); 
  }

  $result = $stmt->get_result();
  $discussions = array();
  while($row = $result->fetch_assoc()) {
    array_push($discussions, array(
      'id' => $row['id'],
      'nickname' => $row['nickname'],
      'content' => $row['content'],
      'created_at' => $row['created_at']
    ));
  }
  $json = array(
    'ok' => true,
    'discussions' => $discussions
  );

  $response = json_encode($json);
  echo $response;
?>

5. hw1_實作留言板「載入更多」功能

MTR05_第十二週(06/28 ~ 07/04):前後端整合_作業一參考範例(請寫完作業再看)12':00" 開始

流程:

  1. 把顯示留言包成一個函式

  2. 16':13" 實作載入更多的按鈕

a. 「載入更多」按鈕要放在 comments 裡面,可以把它獨立成一個字串

b. .on():jQuery 的 addeventlistener

c. 18':17" 先把載入更多的按鈕移除,加上新的留言,再把按鈕加回去

用事件代理的方式

遇到的問題:

  1. 更改檔名之後,要特別注意 url 路徑

  2. Id 不用 escape() 因為我們跳脫的是特殊符號,數字不需要

  3. 老實說看完兩遍影片並跟著實作以後,並沒有很懂裡面的一些細節。

  4. 為什麼會有錯誤訊息:

Access to XMLHttpRequest at 'file:///Users/christy/.bitnami/stackman/machines/xampp/volumes/root/htdocs/christy/p_discussions/index.html' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, chrome-untrusted, https.

是因為我把 url 的名稱改了,但是 ajax 裡面如果名稱有變,應該要完整的把名字寫出來,如果只寫成 url,,就會發生這個錯誤。

要寫完整變成 url: getCommentsURL, 後面接變數名稱

  1. 要怎麼在點擊按鈕時,除了抓留言以外,去偵測留言的數量?

  2. 為什麼在 index.html 裡面,getCommentsAPI 這個函式要放一個 call back fn 當第三個參數?

是為了確保執行順序嗎?例如說一定要先確認是否拿到 siteKey and before 才能繼續下去?這裡不是很明白

function getCommentsAPI(siteKey, before, cb) {
  let url = `http://localhost:8080/christy/p_discussions/api_comments.php?site_key=${siteKey}`
  if (before) {
    url += '&before=' + before
  }
  $.ajax({
    url,
  }).done(function(data) {
    cb(data)
  });
}
  1. 如果看完影片自己硬做的話,就會變成網路上看到的文章先在資料庫新增一個表格存 pageToken,接著把全部的 api 都改掉,然後可能會在哪個坑跌倒,又可能就沒有然後了。

所以說我應該要好好利用資源問問題,不要再亂來了。










Related Posts

Secure Apache Using Certbot with Let's Encrypt on Ubuntu 20.04

Secure Apache Using Certbot with Let's Encrypt on Ubuntu 20.04

資料庫相關專有名詞簡介

資料庫相關專有名詞簡介

【Day04】元件

【Day04】元件


Comments